#include "minicon.h"
#include "miniopt.h"
#include <shellapi.h>

extern int err_ret(int errcode, const char *msg, const WCHAR *name);

//=============================================================================

static char s_confirm = 'y';

#ifndef FIND_FIRST_EX_LARGE_FETCH
#define FIND_FIRST_EX_LARGE_FETCH      0x00000002
#endif

//=============================================================================

static BOOL single_del( const WCHAR *fnm, DWORD attr, bool force )
{
   bool dir = (0 != (attr & FILE_ATTRIBUTE_DIRECTORY));
   bool rdo = (0 != (attr & FILE_ATTRIBUTE_READONLY ));

   if ( rdo && force )
      ::SetFileAttributesW(fnm, attr & ~FILE_ATTRIBUTE_READONLY);

   return (dir)
      ? ::RemoveDirectoryW(fnm)
      : ::DeleteFileW(fnm)
   ;
}

//=============================================================================

static BOOL recursive_del( const WCHAR *base, DWORD attr, bool force )
{
   WCHAR  path[MAX_PATH+10];
   int n0 = ::lstrlenW(base);  if (n0 > 0 && base[n0-1] == L'\\') --n0;
   if ( n0 >= MAX_PATH ) {
b:    ::SetLastError(ERROR_INVALID_NAME);
      return FALSE;
   }
   Win::bmov(path, base, n0*sizeof(WCHAR));
   ::lstrcpyW(path + n0++, L"\\*");

   WIN32_FIND_DATAW ff;
   HANDLE hf = ::FindFirstFileEx(
      path, FindExInfoStandard, &ff, FindExSearchNameMatch, NULL, 
      Win::os_version < 0x601 ? 0 : FIND_FIRST_EX_LARGE_FETCH
   );
   if (INVALID_HANDLE_VALUE != hf) {
      BOOL res;         
      do {
         if ( !Opt::is_dot(ff.cFileName)) {
            if (n0 + ::lstrlenW(ff.cFileName) >= MAX_PATH)
               goto b;

            ::lstrcpyW(path+n0, ff.cFileName);
            bool dir = (0 != (ff.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY    ));
            bool rpt = (0 != (ff.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT));
            if ( dir && !rpt )
               recursive_del(path, ff.dwFileAttributes, force);
            else
               single_del(path, ff.dwFileAttributes, force);
         }
         res = ::FindNextFileW(hf, &ff);
      } while( res );
      ::FindClose(hf);
   }

   return single_del(base, attr, force);
}

//=============================================================================

static int rm(
   WCHAR *fnm, DWORD attr, bool verbose, bool interactive, bool recursive, bool force )
{
   if ( verbose )
   {
      Con::o("remove"); Con::f(L" \"%s\" -- ", fnm);
   }

   if ( interactive )
   {
      if ( verbose ) Con::n();
      Con::o('"'); Con::o(fnm);
      s_confirm = (char)Con::confirm("\" Will be deleted", s_confirm);
      if ( s_confirm != 'y' && s_confirm != 'Y' )
      {
         return 0; // not confirmed
      }
   }

   BOOL res = ( !recursive || 0 == (attr & FILE_ATTRIBUTE_DIRECTORY) )
      ? single_del(fnm, attr, force)
      : recursive_del(fnm, attr, force)
   ;

   if ( verbose )
   {
      Con::o(res ? "ok" : "failed"); Con::n();
   }

   int rc = 0;
   if ( !res )
   {
      rc = err_ret(-22, "Can't delete", fnm);
   }

   return rc;
}

//=============================================================================

int rm(
   WCHAR ** args,int na,
   bool verbose, bool force, bool interactive, bool recursive )
{
   int i, rc;
   WCHAR fnm[MAX_PATH*2], *pw;

   s_confirm = 'y';

   if ( na < 1 )
      return Opt::opt_err(Opt::Err_Missed_Param);
   
   for ( rc = i = 0; i < na && rc == 0; ++i )
   {
      pw = args[i];
      if (Opt::end_with_slash(pw)) {
         if (Opt::is_root(pw)) {
            ::SetLastError(ERROR_INVALID_NAME);
            return err_ret(-20,"Invalid name:",pw);
         }
         Opt::norm_slash(pw, pw, ::lstrlen(pw)+1, -1);
      }
      
      WIN32_FIND_DATAW ff;

      HANDLE hFind = ::FindFirstFileW(args[i], &ff);
      if ( INVALID_HANDLE_VALUE == hFind )
      {
         ::SetLastError(0);
         if ( force ) continue;
         return err_ret(-21, "File does not exist:", args[i]);
      }
      do 
      {
         if (Opt::is_dot(ff.cFileName))
            continue;

         ::lstrcpyW(fnm, args[i]);
         pw = fnm + ::lstrlenW(fnm);
         while ( pw > fnm && pw[-1] != L'\\' && pw[-1] != L'/' && pw[-1] != L':') --pw;
         ::lstrcpyW(pw, ff.cFileName);

         rc = rm( fnm, ff.dwFileAttributes, verbose, interactive, recursive, force );
         if ( rc ) 
            break;

      } while ( ::FindNextFileW(hFind, &ff) );

      ::FindClose(hFind);
   }
   return rc;
}

//=============================================================================
